﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using Crucible.DocSearch;
using Crucible.Models;
using WebMatrix.WebData;

namespace Crucible.Controllers
{
    public class NController : ApiController
    {
        private ArgumentConnection db = new ArgumentConnection();

        // GET api/N
        public IEnumerable<Node> GetNodes()
        {
            var nodes = db.Nodes.Include(n => n.AddedBy);
            return nodes.AsEnumerable();
        }

        // GET api/N/5
        public Object GetNode(int id)
        {
            Node node = db.Nodes.Find(id);
            if (node == null)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            }

            var parents = from link in db.NodeLinks
                          join par in db.Nodes on link.Parent_Id equals par.Id
                          where link.Child_Id == id
                          select par;

            return new {node, parents};
        }

        // GET api/N/5
        public Object GetTree(int id)
        {
            Node node = db.Nodes.Find(id);
            if (node == null)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            }

            return Request.CreateResponse(HttpStatusCode.OK, AssertionHelper.GetTreeObject(node.Id, -1));
        }

        // PUT api/N/5
        [Authorize]
        public HttpResponseMessage PutNode(int id, Node node)
        {
            if (ModelState.IsValid && id == node.Id)
            {
                db.Entry(node).State = EntityState.Modified;

                try
                {
                    db.SaveChanges();
                }
                catch (DbUpdateConcurrencyException)
                {
                    return Request.CreateResponse(HttpStatusCode.NotFound);
                }

                return Request.CreateResponse(HttpStatusCode.OK);
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest);
            }
        }

        

        //[HttpPost]
        //[Authorize]
        //public HttpResponseMessage SaveDraft(int sourceId, bool sendTree, [System.Web.Mvc.ModelBinder(typeof(NodeBinder))] Node node)
        //{

        //    var nodeType = node.GetType();
        //    if (nodeType == typeof(Persuasive))
        //    {
        //        return SavePersuasiveDraft((Persuasive)node, sourceId, sendTree);
        //    }

        //    if (nodeType == typeof(Source))
        //    {
        //        return SaveSourceDraft((Source)node, sourceId, sendTree);
        //    }

        //    if (nodeType == typeof(Originator))
        //    {
        //        return SaveOriginatorDraft((Originator)node);
        //    }

        //    return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "I don't know how to save that.");

        //}

        public void ExplodeAsNecessary(int userId, Node nodeFromDb)
        {
            if (nodeFromDb.AddedBy_UserId != userId)
            {
                throw new HttpException(403, "You can't edit an assertion you don't own.");
            }

            if (nodeFromDb.Published)
            {
                throw new HttpException(403, "You can't edit an assertion which has been published.");
            }
        }

        [HttpPost]
        [Authorize]
        public HttpResponseMessage SaveDraft(Node node, bool sendTree)
        {
            return SaveDraft(node, sendTree, 0);
        }

        [HttpPost]
        [Authorize]
        public HttpResponseMessage SaveDraft(Node node, bool sendTree, int sourceId)
        {
            int userId = WebSecurity.CurrentUserId;
            node.AddedBy_UserId = userId;

            //if (!ModelState.IsValid)
            //{
            //    return View("Details", assertion);
            //}

            var persuasives = new[] { NodeType.Assertion, NodeType.Interpretation };

            if (node.Id > 0)
            {
                var existing = db.Nodes.Find(node.Id);
                if (existing != null)
                {
                    
                    ExplodeAsNecessary(userId, existing);
                    //existing.Attribution = assertion.Attribution;
                    existing.Title = node.Title;
                    existing.Body = node.Body;
                    existing.Type = node.Type;
                    existing.Link = node.Link;
                    db.Entry(existing).State = EntityState.Modified;

                    db.SaveChanges();
                    IndexManager.EditAssertion(existing, WebSecurity.CurrentUserName);
                }
            }
            else
            {
                db.Nodes.Add(node);
                db.SaveChanges();
                IndexManager.AddAssertion(node, WebSecurity.CurrentUserName);
            }

            db.Database.ExecuteSqlCommand("delete from dbo.NodeLinks where Parent_Id = {0}", node.Id);

            if (node.Type == NodeType.Assertion)
            {
                foreach (NodeLink link in AssertionHelper.GetLinks(node.Id, node.Body))
                {
                    var child = db.Nodes.FirstOrDefault(n => n.Id == link.Child_Id);
                    if (child == null) continue;

                    if (persuasives.Contains(child.Type)) // make sure the link is to a persuasive (assertion, interpretation)
                    {
                        db.NodeLinks.Add(link);
                    }
                }
            }
            else if (node.Type == NodeType.Interpretation && sourceId > 0)
            {
                if (ValidTarget(sourceId, NodeType.Source))
                {
                    db.NodeLinks.Add(new NodeLink() {Parent_Id = node.Id, Child_Id = sourceId});
                }

            } else if (node.Type == NodeType.Source && sourceId > 0)
            {
                if (ValidTarget(sourceId, NodeType.Originator))
                {
                    db.NodeLinks.Add(new NodeLink() { Parent_Id = node.Id, Child_Id = sourceId });
                }
            }

            db.SaveChanges();

            if (sendTree)
            {
                return Request.CreateResponse(HttpStatusCode.OK, AssertionHelper.GetTreeObject(node.Id, -1));
            }

            return Request.CreateResponse(HttpStatusCode.OK, node);
            
        }

        private bool ValidTarget(int id, NodeType expected)
        {
            var child = db.Nodes.FirstOrDefault(n => n.Id == id);
            if (child == null) return false;
            return child.Type == expected;
        }


        [HttpPost]
        [Authorize]
        public HttpResponseMessage Publish(Node node)
        {
            return Publish(node, 0);
        }

        [HttpPost]
        [Authorize]
        public HttpResponseMessage Publish(Node node, int sourceId)
        {
            var response = SaveDraft(node, false, sourceId);

            if (response.StatusCode != HttpStatusCode.OK)
            {
                return response;
            }

            var publishProblems = AssertionHelper.TryPublish(node.Id);
            if (publishProblems.Any())
            {
                //return Request.CreateErrorResponse(HttpStatusCode.BadRequest,  )
                return Request.CreateResponse(HttpStatusCode.OK, new {publishProblems});
            }

            return Request.CreateResponse(HttpStatusCode.OK, AssertionHelper.GetTreeObject(node.Id, -1));

        }


        //public HttpResponseMessage SaveSourceDraft(Source src, int sourceId, bool sendTree)
        //{
        //    int userId = WebSecurity.CurrentUserId;
        //    src.AddedBy_UserId = userId;

        //    if (src.Id > 0)
        //    {
        //        var existing = db.Sources.Find(src.Id);
        //        if (existing != null)
        //        {
        //            ExplodeAsNecessary(userId, existing);
        //            //existing.Attribution = assertion.Attribution;
        //            existing.Title = src.Title;
        //            existing.Link = src.Link;
        //            existing.DateOriginated = src.DateOriginated;
        //            db.Entry(existing).State = EntityState.Modified;

        //            db.SaveChanges();
        //            IndexManager.EditAssertion(existing, WebSecurity.CurrentUserName);
        //        }
        //    }
        //    else
        //    {
        //        db.Sources.Add(src);
        //        db.SaveChanges();
        //        IndexManager.AddAssertion(src, WebSecurity.CurrentUserName);
        //    }

        //    db.Database.ExecuteSqlCommand("delete from dbo.NodeLinks where Parent_Id = {0}", src.Id);

        //    if (sourceId > 0 && db.Originators.Any(o => o.Id == sourceId)) // make sure the link is to an originator
        //    {
        //        db.NodeLinks.Add(new NodeLink() { Parent_Id = src.Id, Child_Id = sourceId });
        //    }

        //    db.SaveChanges();

        //    if (sendTree)
        //    {

        //        Request.CreateResponse(HttpStatusCode.OK, AssertionHelper.GetTreeObject(src.Id, -1));
        //    }

        //    return Request.CreateResponse(HttpStatusCode.OK, src);
        //}

        //public HttpResponseMessage SaveOriginatorDraft(Originator origin)
        //{
        //    int userId = WebSecurity.CurrentUserId;
        //    origin.AddedBy_UserId = userId;

        //    if (origin.Id > 0)
        //    {
        //        var existing = db.Originators.Find(origin.Id);
        //        if (existing != null)
        //        {
        //            ExplodeAsNecessary(userId, existing);
        //            existing.Title = origin.Title;
        //            existing.Link = origin.Link;
        //            db.Entry(existing).State = EntityState.Modified;

        //            db.SaveChanges();
        //            IndexManager.EditAssertion(existing, WebSecurity.CurrentUserName);
        //        }
        //    }
        //    else
        //    {
        //        db.Originators.Add(origin);
        //        db.SaveChanges();
        //        IndexManager.AddAssertion(origin, WebSecurity.CurrentUserName);
        //    }

        //    return Request.CreateResponse(HttpStatusCode.OK, origin);
        //}

        //// POST api/N
        //public HttpResponseMessage PostNode(Node node)
        //{
        //    if (ModelState.IsValid)
        //    {
        //        db.Nodes.Add(node);
        //        db.SaveChanges();

        //        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, node);
        //        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = node.Id }));
        //        return response;
        //    }
        //    else
        //    {
        //        return Request.CreateResponse(HttpStatusCode.BadRequest);
        //    }
        //}

        // DELETE api/N/5
        public HttpResponseMessage DeleteNode(int id)
        {
            Node node = db.Nodes.Find(id);
            if (node == null)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }

            db.Nodes.Remove(node);

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }

            return Request.CreateResponse(HttpStatusCode.OK, node);
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}